using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.Unity;
using MiniStock.Infrastructure.Queue;

namespace MiniStock.Infrastructure.Configurator
{
    public class MemoryConfigurator: IEventProcessorConfigurator, IPluginConfigurator
    {
        public int ThreadCount { get; set; }

        [Dependency]
        public IUnityContainer RootContainer { get; set; }

        protected List<List<string>> PluginAffinityMap { get; set; }

        protected List<string> Plugins { get; set; }
        protected List<EventProcessorDescriptor> Processors { get; set; }

        public int GetProcessorNodeCount()
        {
            return 1;
        }


        public void InitEventProcessorDescriptors(List<EventProcessorDescriptor> processors)
        {
            List<string> plugins = GetEntryPoints();
            var processorNodes = processors.Where(i => i.IsProcessorNode).ToList();
            PreparePluginAffinityMap(plugins, processorNodes.Count());
            int ii = 0; // index of current item in processor nodes
            for (int i = 0; i < processors.Count; i++)
            {
                var current = processors[i];
                if(Processors != null)
                {
                    var p = Processors.FirstOrDefault(j => j.EventProcessorName == processors[i].EventProcessorName);
                    if(p != null)
                    {
                        current.Queue = p.Queue;
                        current.Plugins = p.Plugins;
                        current.WorkerThreadCount = p.WorkerThreadCount;
                        continue;
                    }
                } 

                current.Queue = RootContainer.Resolve<IEventQueue>();
                
                current.Queue.Address = GetQueueAddressForNode(current.EventProcessorName);
                current.WorkerThreadCount = GetWorkerThreadCountForNode(current.EventProcessorName);
                if (current.IsProcessorNode)
                {
                    current.Plugins = GetPluginsForNode(ii++);
                }
            }
            Processors = processors;
        }

        public string GetInputQueueRef(string processorName)
        {
            return processorName + ".in";
        }

        public string GetOutputQueueRef(string processorName)
        {
            return processorName + ".out";
        }

        public string GetName()
        {
            throw new NotImplementedException();
        }

        public List<string> GetPluginRefs(string processorName)
        {
            return new List<string> {};
        }

        public int GetThreadCount(string nodeName)
        {
            if(ThreadCount == default(int))
                return 1;
            else
                return ThreadCount;
        }

        private int GetWorkerThreadCountForNode(string nodeName)
        {
            return 1;
        }

        protected virtual void PreparePluginAffinityMap(List<string> plugins, int processorCount)
        {
            PluginAffinityMap = new List<List<string>>(processorCount);
            for(int i=0;i<processorCount;i++)
                PluginAffinityMap.Add(new List<string>());
            
            for(int i=0;i<plugins.Count;i++)
            {
                PluginAffinityMap[i % processorCount].Add(plugins[i]);
            }
        }

        protected virtual string GetQueueAddressForNode(string processorName)
        {
            return "";
        }

        protected virtual List<string> GetPluginsForNode(int index)
        {
            return PluginAffinityMap[index];
        }

        public List<string> GetEntryPoints()
        {
            var result = new List<string>();
            foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(i => !i.IsDynamic)) 
            foreach(var type in assembly.GetExportedTypes())
            {
                if(type.IsClass && 
                    !type.IsAbstract && 
                    typeof (IProcessorPluginEntryPoint).IsAssignableFrom(type))
                    result.Add(type.AssemblyQualifiedName);
            }
            return result;
        }

        public string GetEntryPoint(string name)
        {
            throw new NotImplementedException();
        }
    }
}